#include "drivers/chip/fmc.h"
#include "sys_types.h"

#define SYS_LOG_DOMAIN "qemu"
#include "sys_log.h"

#define _SECTOR_SIZE 0x100

static uint8_t _enable = 0;
static uint8_t _locked = 1;
static uint8_t _read_protected = 0;
static uint8_t _write_protected = 0;
static __no_init uint8_t _virtual_rom[1000000];

void drv_fmc_enable(void)
{
    SYS_LOG("");
    _enable = 1;
}

void drv_fmc_disable(void)
{
    SYS_LOG("");
    _enable = 0;
}

void drv_fmc_lock(void)
{
    SYS_LOG("");
    _locked = 1;
}

void drv_fmc_unlock(void)
{
    SYS_LOG("");
    _locked = 0;
}

unsigned drv_fmc_get_rom_base(void)
{
    SYS_LOG("");
    return (unsigned)_virtual_rom;
}

unsigned drv_fmc_get_rom_size(void)
{
    SYS_LOG("");
    return sizeof(_virtual_rom);
}

unsigned drv_fmc_get_sector_base(unsigned addr)
{
    return addr / _SECTOR_SIZE * _SECTOR_SIZE;
}

unsigned drv_fmc_get_sector_size(unsigned addr)
{
    SYS_LOG("size = 0x%X", _SECTOR_SIZE);
    return _SECTOR_SIZE;
}

int drv_fmc_sector_erase(unsigned addr)
{
    SYS_LOG("addr = 0x%X", addr);
    if (_enable == 0 || _locked != 0)
    {
        return -1;
    }
    memset(&_virtual_rom[addr / _SECTOR_SIZE * _SECTOR_SIZE], -1u, _SECTOR_SIZE);
    return 0;
}

int drv_fmc_data_write(unsigned addr, const void *src, unsigned size)
{
    SYS_LOG("addr = 0x%X, size = %d", addr, size);

    if (_enable == 0 || _locked != 0)
    {
        return -1;
    }

    for (unsigned i = 0; i < size; i++)
    {
        _virtual_rom[addr + i] &= ((uint8_t *)src)[i];
    }

    for (unsigned i = 0; i < size; i++)
    {
        if (_virtual_rom[addr + i] != ((uint8_t *)src)[i])
        {
            return -1;
        }
    }

    return 0;
}

int drv_fmc_data_read(void *dest, unsigned addr, unsigned size)
{
    SYS_LOG("addr = 0x%X, size = %d", addr, size);

    if (_enable == 0 || _locked != 0)
    {
        return -1;
    }

    for (unsigned i = 0; i < size; i++)
    {
        ((uint8_t *)dest)[i] = _virtual_rom[addr + i];
    }

    return 0;
}

int drv_fmc_set_read_protection(bool enable)
{
    SYS_LOG("%s", enable ? "enable" : "disable");

    if (_enable == 0 || _locked != 0)
    {
        return -1;
    }

    _read_protected = enable;

    return 0;
}

int drv_fmc_set_write_protection(bool enable, unsigned start_offset, unsigned size)
{
    SYS_LOG("%s", enable ? "enable" : "disable");

    if (_enable == 0 || _locked != 0)
    {
        return -1;
    }

    _write_protected = enable;

    return 0;
}

bool drv_fmc_is_read_protection(void)
{
    SYS_LOG("");
    return _read_protected;
}

bool drv_fmc_is_write_protection(unsigned addr)
{
    SYS_LOG("");
    return _write_protected;
}
